#include <QDateTime>
#include <QThread>
#include <QDir>
#include <QSqlQuery>
#include "frasserver.h"
#include "ui_frasserver.h"

FrasServer::FrasServer(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::FrasServer) {
    ui->setupUi(this);
    // QTcpServer在有新客户端连接时会发送newconnection信号，此处关联了接收客户端连接的槽函数逻辑
    connect(&server_, &QTcpServer::newConnection, this, [this](){
        // 获取与客户端通信的套接字
        qDebug() << "接收到客户端连接\n";
        socket_ = server_.nextPendingConnection();
        // 当客户端有数据到达时会发送readyRead信号，此处通过这个信号接收和处理数据
        connect(socket_, &QTcpSocket::readyRead, this, &FrasServer::readData);
    });
    // 绑定本地所有地址并开始监听
    server_.listen(QHostAddress::Any, 15213);
    data_size_ = 0;
    tbl_model_.setTable("staff_info");  // 绑定数据表
    // 将fobj移动到线程中执行
    FaceObject::getInstance()->moveToThread(&face_query_thread_);
    face_query_thread_.start();
    // 参数1：发送信号的类，参数2：发送信号的函数，参数3：接收信号的类，参数4：接收信号的槽函数
    connect(this, &FrasServer::queryInfo, FaceObject::getInstance(), &FaceObject::faceQuery);    // 关联到fobj查询逻辑的信号
    connect(FaceObject::getInstance(), &FaceObject::sendFaceID, this, &FrasServer::recvFaceID);  // 关联接收发送faceID信号的槽函数
    // 在当前目录下创建相关文件用于保存图片
    QDir d;
    d.mkdir("takephotos");  // takephotos目录临时保存注册界面拍照产生的照片，程序退出时会自动清除这些图片
    d.mkdir("faceimg");     // faceimg保存所有注册成功的人脸图像
}

FrasServer::~FrasServer() {
    delete ui;
    // 删除tph目录及该目录下所有临时创建的图片
    QDir tph_dir;
    if (!tph_dir.cd(tph_dir.path() + "/takephotos")) {
        qDebug() << "~FrasServer(): 切换到临时目录失败！\n";
    }
    QFileInfoList tph_flist = tph_dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files);
    for (auto &photo_file : tph_flist) {
        if (!tph_dir.remove(photo_file.fileName())) {
            qDebug() << "~FrasServer(): 删除文件 " << photo_file.fileName() << "失败！\n";
        }
    }
}

void FrasServer::readData() {
    // 接收客户端发送的人脸数据
    qDebug() << "接收到客户端数据\n";
    QDataStream stream(socket_);  // 将套接字绑定到数据流
    stream.setVersion(QDataStream::Qt_5_15);
    if (data_size_ == 0) {        // data_size为0说明还未获取到数据
        if (socket_->bytesAvailable() < static_cast<qint64>(sizeof(data_size_))) {
            return;               // 数据不够凑齐长度，无效数据
        }
        stream >> data_size_;     // 采集数据长度
    }
    // 数据长度未达到预期大小时说明数据还未发送完成，返回继续等待
    if (socket_->bytesAvailable() < static_cast<qint64>(data_size_)) {
        return;
    }
    // 读取数据
    QByteArray recv_data;
    stream >> recv_data;
    data_size_ = 0;  // 重置大小标识以用于读取下一轮数据
    if (recv_data.size() == 0) {  // 未读取到数据
        return;
    }

    // 在控件上显示图片
    QPixmap pmp;
    pmp.loadFromData(recv_data, "jpg");
    pmp = pmp.scaled(ui->photoLB->size(), Qt::KeepAspectRatioByExpanding);
    int ow = pmp.width();
    int oh = pmp.height();
    int sx = (ow - 670) / 2;
    int sy = (oh - 650) / 2;
    pmp = pmp.copy(sx, sy, 670, 650);  // 裁剪人脸图像以使其适配控件大小
    ui->photoLB->setPixmap(pmp);

    // 读取人脸信息并执行人脸查询
    cv::Mat face_img;
    std::vector<uchar> decode_data;
    decode_data.resize(recv_data.size());
    memcpy(decode_data.data(), recv_data.data(), recv_data.size());  // 创建对应类型变量
    face_img = cv::imdecode(decode_data, cv::IMREAD_COLOR);
    emit queryInfo(face_img);  // 发射查询信号到fobj的人脸查询槽函数以查询人脸ID
}

void FrasServer::recvFaceID(int64_t face_id) {
    // 判断是否查询成功
    QString send_data("");  // 存待发送数据
    if (face_id < 0) {  // face_id小于0说明查询失败，直接返回空数据
        qDebug() << "FrasServer::recvFaceID(): 人脸查询失败，未匹配到人脸！\n";
        socket_->write(send_data.toUtf8());
        return;
    }
    // 从数据表中查询face_id对应的个人信息
    tbl_model_.setFilter(QString("staff_face_id=%1").arg(face_id));  // 设置查询过滤器
    tbl_model_.select();
    if (tbl_model_.rowCount() == 0) {
        qDebug() << "FrasServer::recvFaceID(): 员工信息查询失败，不存在对应当前人脸的数据条目！\n";
    }
    QSqlRecord record = tbl_model_.record(0);  // 获取数据记录

    // 将数据插入到考勤数据表attendance_info
    QSqlQuery q;
    if (q.exec(QString("INSERT INTO attendance_info (staff_id) VALUES (%1);").arg(record.value(0).toString()))) {
        // 将数据记录转换为json格式（编号，姓名，部门，时间，头像）
        send_data = QString("{\"staff_id\":\"%1\",\"staff_name\":\"%2\",\"staff_depa\":\"%3\",\"time\":\"%4\"}")
                        .arg(record.value(0).toString())
                        .arg(record.value(1).toString())
                        .arg(record.value(8).toString())
                        .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
    } else {  // 执行失败时返回空json数据作为失败标识
        qDebug() << "FrasServer::recvFaceID(): 考勤信息写入到数据库失败！\n";
    }
    // 将员工信息数据发送回客户端
    socket_->write(send_data.toUtf8());
}
